/////////////////////////////////////////////////////////////////////////////
// Name:        ax_module_audio.cpp
// Purpose:     Audio module classes
// Author:      Julian Smart
// Modified by:
// Created:     2009-03-20
// RCS-ID:
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "ax/ax_pagerctrl.h"
#include "ax/ax_view_summary.h"
#include "ax/ax_utils.h"
#include "ax/ax_activator.h"

#include "ax_resource/ax_resource_library.h"
#include "ax_resource/ax_module_audio.h"
#include "ax_resource/ax_mp3info.h"
#include "ax_resource/ax_activator_resource.h"
#include "ax_resource/ax_mediactrlpanel.h"
#include "ax_resource/ax_menu_resource.h"

/*
 * AxAudioResource implementation.
 */

IMPLEMENT_DYNAMIC_CLASS(AxAudioResource, AxResource)

AxAudioResource::~AxAudioResource()
{
}

void AxAudioResource::Init()
{
}

// Copy the basic resource information.
void AxAudioResource::Copy(const AxAudioResource& resource)
{
    AxResource::Copy(resource);
}

// Copy from MP3 information
void AxAudioResource::CopyAlbumInfo(const AxMP3Info& info)
{
    SetFilename(info.GetFileName());
    SetProperty(axPROPERTY_AUTHOR, info.GetArtist());
    SetProperty(axPROPERTY_GENRE, AxMP3Info::GetGenreString(info.GetGenreIndex()));
    SetProperty(axPROPERTY_CREATION_DATE, info.GetYear());
    SetProperty(axPROPERTY_DESCRIPTION, info.GetComment());

    wxString title(info.GetAlbum());
    if (title.IsEmpty())
        title = info.GetTitle();

    SetProperty(axPROPERTY_TITLE, title);
}

// Copy from MP3 information
void AxAudioResource::CopyTrackInfo(const AxMP3Info& info)
{
    SetFilename(info.GetFileName());
    SetProperty(axPROPERTY_AUTHOR, info.GetArtist());
    SetProperty(axPROPERTY_GENRE, AxMP3Info::GetGenreString(info.GetGenreIndex()));
    SetProperty(axPROPERTY_TITLE, info.GetTitle());
    SetProperty(axPROPERTY_CREATION_DATE, info.GetYear());
    SetProperty(axPROPERTY_DESCRIPTION, info.GetComment());
    SetProperty(axPROPERTY_TRACK_POSITION, info.GetTrack());
}

/*
 * AxAudioModule implementation.
 */

IMPLEMENT_DYNAMIC_CLASS(AxAudioModule, AxModule)

// Constructor.
AxAudioModule::AxAudioModule()
{
}

// Called on IDE startup, before the main window has been created.
bool AxAudioModule::OnInit()
{
    return true;
}

// Called on application startup, after the main window has been created.
bool AxAudioModule::OnInitUI()
{
    return true;
}

// Called just before program termination, but only if OnInit()
// succeeded.
void AxAudioModule::OnExit()
{
}

// Read config settings
bool AxAudioModule::LoadConfig(wxConfigBase& config)
{
    wxUnusedVar(config);
    return true;
}

// Write config settings
bool AxAudioModule::SaveConfig(wxConfigBase& config)
{
    wxUnusedVar(config);
    return true;
}

// Is this file supported?
bool AxAudioModule::IsFileSupported(const wxString& filename)
{
    wxString ext(AxFileUtilities::GetExtension(filename).Lower());
    if (GetLibrary()->GetSettings().GetSupportedFiles().IsEnabled(ext) && (ext == wxT("mp3") || ext == wxT("wav") || ext == wxT("wmv") || ext == wxT("ogg") || ext == wxT("aac")))
        return true;
    else
        return false;
}

// Load the file into a new AxResource
AxResource* AxAudioModule::LoadResource(const wxString& filename)
{
    wxString ext = AxFileUtilities::GetExtension(filename).Lower();
    if (ext == wxT("mp3"))
    {
        AxMP3Info mp3Info(filename);

        wxString album, trackName;
        wxString title;

        AxAudioResource* resource = new AxAudioResource;
        resource->CopyAlbumInfo(mp3Info);

        if (!mp3Info.GetAlbum().IsEmpty() && !mp3Info.GetTitle().IsEmpty())
            title = mp3Info.GetAlbum() + wxT(" - ") + mp3Info.GetTitle();
        else if (!mp3Info.GetAlbum().IsEmpty())
            title = mp3Info.GetAlbum();
        else
            title = mp3Info.GetTitle();
        resource->SetProperty(axPROPERTY_TITLE, title);

        AxAudioResource* childResource = new AxAudioResource;
        childResource->CopyTrackInfo(mp3Info);
        childResource->SetFilename(filename);

        resource->AddChild(childResource);

        return resource;
    }
    else
    {
        AxAudioResource* resource = new AxAudioResource;
        resource->SetProperty(axPROPERTY_TITLE, wxFileNameFromPath(filename));

        AxAudioResource* childResource = new AxAudioResource;
        childResource->SetFilename(filename);
        childResource->SetProperty(axPROPERTY_TITLE, wxFileNameFromPath(filename));

        resource->AddChild(childResource);

        return resource;
    }
}

// Aggregate any files that belong together and add to library. Return false if you don't
// aggregate any files.
bool AxAudioModule::AggregateFiles(AxResourceLibrary* library, wxArrayString& files, bool checkIfFileLoaded)
{
    wxArrayString albums;
    AxResourceArray albumBooks;

    // TODO: sort MP3 files in correct play order

    size_t i = 0;
    while (i < files.GetCount())
    {
        bool processedThis = false;

        wxString f(files[i]);
        wxString ext(AxFileUtilities::GetExtension(f).Lower());
        if (ext == wxT("mp3"))
        {
            AxMP3Info mp3Info(f);

            if (!mp3Info.GetAlbum().IsEmpty())
            {
                AxAudioResource* trackResource = new AxAudioResource;
                trackResource->CopyTrackInfo(mp3Info);

                int idx = albums.Index(mp3Info.GetAlbum());
                if (idx == wxNOT_FOUND)
                {
                    albums.Add(mp3Info.GetAlbum());

                    AxAudioResource* resource = new AxAudioResource;
                    resource->CopyAlbumInfo(mp3Info);
                    resource->AddChild(trackResource);

                    albumBooks.Add(resource);
                }
                else
                {
                    AxResource* resource = albumBooks[idx];
                    resource->AddChild(trackResource);
                }

                processedThis = true;
            }
        }
        // For now, assume all other music files should be aggregated if in the same folder.
        else if (ext == wxT("wav") || ext == wxT("wmv") || ext == wxT("ogg") || ext == wxT("aac"))
        {
            wxString albumName = wxFileNameFromPath(wxPathOnly(f));
            if (!albumName.IsEmpty())
            {
                int idx = albums.Index(albumName);

                AxAudioResource* trackResource = new AxAudioResource;
                
                trackResource->SetFilename(f);
                trackResource->SetProperty(axPROPERTY_TITLE, wxFileNameFromPath(f));

                if (idx == wxNOT_FOUND)
                {
                    albums.Add(albumName);
                    
                    AxAudioResource* resource = new AxAudioResource;
                    resource->SetFilename(f);
                    resource->SetProperty(axPROPERTY_TITLE, albumName);
                    resource->AddChild(trackResource);

                    albumBooks.Add(resource);
                }
                else
                {
                    AxResource* resource = albumBooks[idx];
                    resource->AddChild(trackResource);

                    albumBooks.Add(resource);
                }

                processedThis = true;
            }
        }

        if (processedThis)
            files.RemoveAt(i);
        else
            i ++;
    }

    for (i = 0; i < albums.GetCount(); i++)
    {
        wxString album(albums[i]);
        AxResource* resource = albumBooks[i];
        AxResource* firstResource = NULL;
        if (resource && resource->GetCount() > 0)
            firstResource = resource->GetChild(0);

        // TODO
        // info.SortParts();

        if (!checkIfFileLoaded || (firstResource && library->FindByFile(firstResource->GetFilename()) == -1))
        {
#if 0            
            if (!resource->m_thumbnailImageBlock.Ok())
            {
                LoadImageFromCoverDesign(resource, m_summaryCoverSize);
            }
#endif
            
            library->AddResource(resource);
        }
        else
            delete resource;
    }
    
    return albums.GetCount() > 0;
}

// Create an activator for the resource, for creating views and further menu items.
AxActivator* AxAudioModule::CreateActivator(AxResourceLibrary* library, AxResource* resource)
{
    AxAudioResource* audioResource = wxDynamicCast(resource, AxAudioResource);
    if (audioResource)
    {
        AxAudioResourceActivator* activator = new AxAudioResourceActivator(library, audioResource);
        return activator;
    }
    else
        return NULL;
}

/*
 * AxAudioResourceActivator implementation.
 */

IMPLEMENT_DYNAMIC_CLASS( AxAudioResourceActivator, AxResourceActivator )

AxAudioResourceActivator::AxAudioResourceActivator(AxResourceLibrary* library, AxResource* resource)
{
    m_library = library;
    m_resource = resource;
}

// Operations

void AxAudioResourceActivator::Init()
{
}

void AxAudioResourceActivator::Copy(const AxAudioResourceActivator& activator)
{
    AxResourceActivator::Copy(activator);
}

AxMenu* AxAudioResourceActivator::Activate(AxMenuController* controller, AxMenuItem* item)
{
    // Activate the audio view, with tracks and player.
    AxAudioView* view = wxDynamicCast(controller->GetViewManager()->FindOrCreateView(CLASSINFO(AxAudioView)), AxAudioView);
    if (view)
    {
        controller->GetViewManager()->ActivateView(view, m_resource);
    }

    AxMenu* menu = new AxMenu;
    menu->SetLabel(item->GetLabel());
    menu->SetDescription(item->GetDescription());

    AxMenuItem* menuItem = new AxMenuItem(_("Play All"), _("Plays all tracks"),
        wxEmptyString,
        new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandAlbumPlayAll, m_library, m_resource));
    menuItem->SetData(new AxMenuItemResourceData(m_resource));
    menuItem->SetStyle(AxMenuStyleBold);
    menu->AddItem(menuItem);
    
    menuItem = new AxMenuItem(_("Stop"), _("Stops playing the current track"),
        wxEmptyString,
        new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandAlbumStop, m_library, m_resource));
    menuItem->SetData(new AxMenuItemResourceData(m_resource));
    menuItem->SetStyle(AxMenuStyleBold);
    menu->AddItem(menuItem);
    
    menuItem = new AxMenuItem(_("Previous Track"), _("Plays the previous track"),
        wxEmptyString,
        new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandAlbumPreviousTrack, m_library, m_resource));
    menuItem->SetData(new AxMenuItemResourceData(m_resource));
    menuItem->SetStyle(AxMenuStyleBold);
    menu->AddItem(menuItem);
    
    menuItem = new AxMenuItem(_("Next Track"), _("Plays the next track"),
        wxEmptyString,
        new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandAlbumNextTrack, m_library, m_resource));
    menuItem->SetData(new AxMenuItemResourceData(m_resource));
    menuItem->SetStyle(AxMenuStyleBold);
    menu->AddItem(menuItem);
    
    menuItem = new AxMenuItem(_("Show Album Details"), _("Shows details of this album"),
        wxEmptyString,
        new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandShowDetails, m_library, m_resource));
    menuItem->SetData(new AxMenuItemResourceData(m_resource));
    menuItem->SetStyle(AxMenuStyleBold);
    menu->AddItem(menuItem);
    
    // Add an 'Up' item
    menu->AddItem(new AxGoUpMenuItem);

    size_t i;
    for (i = 0; i < m_resource->GetCount(); i++)
    {
        AxResource* childResource = m_resource->GetChild(i);
        wxString title = childResource->GetPropertyString(axPROPERTY_TITLE);
        if (title.IsEmpty())
            title = wxT("<unknown>");
        wxString description = childResource->GetLongDescription();
        if (description.IsEmpty())
            description = childResource->GetShortDescription();
        if (description.IsEmpty())
            description = title;
        
        AxMenuItem* menuItem  = new AxMenuItem(title, description,
            wxEmptyString, NULL);
        menuItem->SetData(new AxMenuItemResourceData(childResource));        

        // Create a submenu for playing, showing details, etc.
        {
            AxMenu* submenu = new AxMenu;
            submenu->SetLabel(title);
            submenu->SetDescription(description);
        
            AxMenuItem* subMenuItem = new AxMenuItem(_("Play"), _("Plays this track"),
                wxEmptyString,
                new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandPlay, m_library, childResource));
            subMenuItem->SetStyle(AxMenuStyleBold);
            subMenuItem->SetData(new AxMenuItemResourceData(childResource));
            submenu->AddItem(subMenuItem);

            subMenuItem = new AxMenuItem(_("Stop"), _("Stops playing this track"),
                wxEmptyString,
                new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandStop, m_library, childResource));
            subMenuItem->SetStyle(AxMenuStyleBold);
            subMenuItem->SetData(new AxMenuItemResourceData(childResource));
            submenu->AddItem(subMenuItem);

            subMenuItem = new AxMenuItem(_("Show Track Details"), _("Shows details of this track"),
                wxEmptyString,
                new AxAudioResourceCommandActivator(AxAudioResourceCommandActivator::AxAudioResourceCommandShowDetails, m_library, childResource));
            subMenuItem->SetStyle(AxMenuStyleBold);
            subMenuItem->SetData(new AxMenuItemResourceData(childResource));
            submenu->AddItem(subMenuItem);

            // Add an 'Up' item
            submenu->AddItem(new AxGoUpMenuItem);

            menuItem->SetSubMenu(submenu);
        }

        menu->AddItem(menuItem);
    }
    
    return menu;
}

// Called when a menu is reactivated, for example by going up. This
// allows a view to be shown again, without generating all the menus.
bool AxAudioResourceActivator::Reactivate(AxMenuController* controller, AxMenuItem* WXUNUSED(item))
{
    // Activate the audio view, with tracks and player.
    AxAudioView* view = wxDynamicCast(controller->GetViewManager()->FindOrCreateView(CLASSINFO(AxAudioView)), AxAudioView);
    if (view && !view->IsActive())
    {
        controller->GetViewManager()->ActivateView(view, m_resource);
    }
    return true;
}

/*
 * AxAudioResourceCommandActivator implementation.
 */

IMPLEMENT_DYNAMIC_CLASS( AxAudioResourceCommandActivator, AxResourceActivator )

AxAudioResourceCommandActivator::AxAudioResourceCommandActivator(AxAudioResourceCommand cmd, AxResourceLibrary* library, AxResource* resource):
    AxResourceActivator(library, resource)
{
    m_command = cmd;
}

// Operations

void AxAudioResourceCommandActivator::Init()
{
    m_command = AxAudioResourceCommandUnknown;
}

void AxAudioResourceCommandActivator::Copy(const AxAudioResourceCommandActivator& activator)
{
    AxResourceActivator::Copy(activator);

    m_command = activator.m_command;
}

AxMenu* AxAudioResourceCommandActivator::Activate(AxMenuController* controller, AxMenuItem* WXUNUSED(item))
{
    AxAudioView* view = wxDynamicCast(controller->GetViewManager()->FindOrCreateView(CLASSINFO(AxAudioView)), AxAudioView);

    // Track

    if (m_command == AxAudioResourceCommandPlay)
    {
        view->PlayOrPause(m_resource);
    }
    else if (m_command == AxAudioResourceCommandStop)
    {
        view->Stop();
    }
    else if (m_command == AxAudioResourceCommandShowDetails)
    {
        view->ShowTrackDetails(m_resource);
    }

    // Album

    else if (m_command == AxAudioResourceCommandAlbumShowDetails)
    {
        view->AlbumShowDetails(m_resource);
    }
    else if (m_command == AxAudioResourceCommandAlbumStop)
    {
        view->Stop();
    }
    else if (m_command == AxAudioResourceCommandAlbumPlayAll)
    {
        view->AlbumPlayAll(m_resource);
    }
    else if (m_command == AxAudioResourceCommandAlbumNextTrack)
    {
        view->AlbumNextTrack(m_resource);
    }
    else if (m_command == AxAudioResourceCommandAlbumPreviousTrack)
    {
        view->AlbumPreviousTrack(m_resource);
    }

    return NULL;
}

/*
 * AxAudioView implementation.
 */

IMPLEMENT_DYNAMIC_CLASS( AxAudioView, AxView )

void AxAudioView::Init()
{
}

AxAudioResource* AxAudioView::GetResource() const
{
    AxAudioResource* resource = wxDynamicCast(GetDocument(), AxAudioResource);
    return resource;
}

bool AxAudioView::Activate(AxViewManager* viewManager, AxDocument* doc)
{
    // In case the window hasn't been created yet
    Initialize(viewManager);

    AssociateDocument(doc);

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        ShowWindow(viewManager);

        if (doc)
            SetPlaylist();
    }

    return true;
}

bool AxAudioView::Deactivate(AxViewManager* WXUNUSED(viewManager))
{
    // TODO: clear the UI
    return true;
}

void AxAudioView::ShowViewIfInactive()
{
    if (!IsActive())
        GetViewManager()->ActivateView(this, GetDocument());
}

bool AxAudioView::Initialize(AxViewManager* viewManager)
{
    if (!GetWindow())
    {
        AxMediaCtrlPanel* panel = wxDynamicCast(viewManager->GetViewPagerCtrl()->FindPageWindow(wxT("Media")), AxMediaCtrlPanel);
        if (!panel)
        {
            panel = new AxMediaCtrlPanel(viewManager->GetViewPagerCtrl(), wxID_ANY);
            viewManager->GetViewPagerCtrl()->AddPage(panel, wxT("Media"));
        }
        SetWindow(panel);
    }
    return true;
}

bool AxAudioView::Uninitialize(AxViewManager* WXUNUSED(viewManager))
{
    return true;
}

AxMediaCtrlPanel* AxAudioView::GetMediaCtrlPanel() const
{
    return wxDynamicCast(GetWindow(), AxMediaCtrlPanel);
}

void AxAudioView::SetPlaylist()
{
    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel && GetResource())
    {
        panel->SetPlaylist(* GetResource());
    }
}

bool AxAudioView::PlayOrPause(AxResource* resource)
{
    ShowViewIfInactive();

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        int idx = panel->FindByFilename(resource->GetFilename());
        bool thisIsSelected = !((idx == -1) || (panel->GetCurrentItem() != idx));

        if (thisIsSelected)
        {
            if (panel->IsPlaying())
            {
                panel->Pause();
            }
            else if (panel->IsPaused())
            {
                panel->Resume();
            }
            else
            {
                panel->SetAutoPlay(false);
                if (idx != -1)
                    panel->Play(idx);
            }
        }
        else
        {
            if (panel->IsPlaying() || panel->IsPaused())
            {
                panel->Stop();
            }
            if (idx != -1)
            {
                panel->SetAutoPlay(false);
                panel->Play(idx);
            }
        }

        return true;
    }
    else
        return false;
}

bool AxAudioView::Stop()
{
    ShowViewIfInactive();

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        if (panel->IsPlaying() || panel->IsPaused())
            panel->Stop();
        return true;
    }
    else
        return false;
}

bool AxAudioView::ShowTrackDetails(AxResource* resource)
{
    AxSummaryView* view = wxDynamicCast(GetViewManager()->FindOrCreateView(CLASSINFO(AxSummaryView)), AxSummaryView);
    if (view)
    {
        GetViewManager()->ActivateView(view, NULL);

        wxString text;
        size_t i;
        for (i = 0; i < (size_t) resource->GetPropertyCount(); i++)
        {
            wxString name = resource->GetPropertyPtr(i)->GetName();
            wxString value = resource->GetPropertyPtr(i)->GetValue();
            if (!value.IsEmpty())
            {
                text << name << wxT(": ") << value << wxT("\n");
            }
        }
        view->SetDescription(text);
    }

    return true;
}

bool AxAudioView::AlbumShowDetails(AxResource* resource)
{
    ShowTrackDetails(resource);
    return true;
}

bool AxAudioView::AlbumPlayAll(AxResource* resource)
{
    wxUnusedVar(resource);

    ShowViewIfInactive();

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        if (panel->IsPlaying() || panel->IsPaused())
        {
            panel->Stop();
        }
        panel->SetAutoPlay(true);
        panel->Play(0);
        return true;
    }
    else
        return false;
}

bool AxAudioView::AlbumPreviousTrack(AxResource* resource)
{
    wxUnusedVar(resource);

    ShowViewIfInactive();

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        panel->GoToPrevious();
    }
    return true;
}

bool AxAudioView::AlbumNextTrack(AxResource* resource)
{
    wxUnusedVar(resource);

    ShowViewIfInactive();

    AxMediaCtrlPanel* panel = GetMediaCtrlPanel();
    if (panel)
    {
        panel->GoToNext();
    }
    return true;
}
